#include "surveyor_view.hpp"
#include <fmt/format.h>
#include <imgui/imgui.h>
#include <gui/controls/calendar.hpp>
#include <json/json.hpp>
#include <unordered_map>
#include <utils/date_utils.hpp>
#include <utils/misc_utils.hpp>
#include <utils/log.hpp>
#include "app/app_base.hpp"
#include "data/app_config.hpp"
#include "dexode/EventBus.hpp"
#include "extra/surveyor_respondent.hpp"
#include "gui/controls/custom_controls.hpp"

struct RespondData {
    struct TagInfo {
        std::string name;
        double      total;

        std::vector<TagInfo> children;
    };

    std::string          hostname;
    std::string          username;
    std::string          date_start;
    std::string          date_end;
    double               hours;
    std::vector<TagInfo> tags;
};

struct SurveyorView::Private {
    Listener                                     bus {App->Bus ()};
    bool                                         surveyor_enabled {};
    double                                       custom_total {};
    std::unordered_map<std::string, RespondData> responds;

    void LoadConfig ()
    {
        surveyor_enabled = !Config::Value (Config::KEY_SURVEYOR_SERVER_EN).empty ();

        SurveyorRespondent::config_surveyor (surveyor_enabled);
        SurveyorRespondent::config_respondent (Config::Value (Config::KEY_SURVEYOR_SERVER_IP));
    }

    void StoreRespond (RespondData &&data)
    {
        auto key      = fmt::format ("{}@{}", data.username, data.hostname);
        responds[key] = data;
    }
};

unsigned SurveyorView::Flags () const
{
    return eTab;
}

void SurveyorView::RenderContent (ComponentFlag flag)
{
    if (flag == eTab)
        Tab ();
}

const char *SurveyorView::GetName (ComponentFlag flag)
{
    if (flag == eTab)
        return TAB_NAME_OF (AppEvent::SelectTabEvent::eSurveyor);
    return nullptr;
}

bool SurveyorView::Show(ComponentFlag flag) const 
{
    if (flag == eTab) {
        return d->surveyor_enabled;
    }
    return UiComponent::Show(flag);
}

static void RenderTree(double total, const RespondData::TagInfo& tag)
{
    using namespace ImGui;

    TableNextRow();
    TableNextColumn();

    if (!tag.children.empty())
    {
        const bool open = TreeNodeEx(tag.name.c_str(),
            ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_AllowOverlap);
        TableNextColumn();
        Text("%7.2f小时（%.4f天）", tag.total, tag.total / 8.0);
        TableNextColumn();
        Text("%8.4f%%", 100.0 * tag.total / total);

        if (open)
        {
            double sum = 0.0;
            for (auto& x : tag.children)
            {
                sum += x.total;
                PushID(&x);
                RenderTree(total, x);
                PopID();
            }
            if (sum < tag.total)
            {
                PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 0, 255));
                TableNextRow();
                TableNextColumn();
                Bullet();
                SameLine();
                Selectable("未分类", false,
                    ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
                TableNextColumn();
                Text("%7.2f小时（%.4f天）", tag.total - sum, (tag.total - sum) / 8.0);
                TableNextColumn();
                Text("%8.4f%%", (tag.total - sum) / total * 100.0);
                TableNextColumn();
                PopStyleColor(1);
            }
            TreePop();
        }
    }
    else
    {
        Bullet();
        SameLine();
        Selectable(tag.name.c_str(), false,
            ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
        TableNextColumn();
        Text("%7.2f小时（%.4f天）", tag.total, tag.total / 8.0);
        TableNextColumn();
        Text("%8.4f%%", 100.0 * tag.total / total);
    }
}

void SurveyorView::Tab ()
{
    ImGui::AlignTextToFramePadding ();
    if (!d->surveyor_enabled)
    {
        ImGui::CenteredText ("已禁用，需要在程序设置中开启Surveyor服务端");
    }
    else
    {
        static std::string date_s = GetToday (), date_e = GetToday ();
        static DateEditor  date_se ("##DateStart");
        static DateEditor  date_ee ("##DateEnd");
        if (date_se.RenderAsPopup (false))
        {
            date_s = date_se.GetSelectedDate ();
        }
        if (date_ee.RenderAsPopup (false))
        {
            date_e = date_ee.GetSelectedDate ();
        }
        ImGui::Text ("调查开始日期: %s", date_s.c_str ());
        ImGui::SameLine ();
        if (ImGui::Button ("更改##Start"))
            date_se.Show ();
        ImGui::SameLine ();
        ImGui::Text ("调查结束日期: %s", date_e.c_str ());
        ImGui::SameLine ();
        if (ImGui::Button ("更改##End"))
            date_ee.Show ();
        ImGui::SameLine ();
        static std::string quick_s, quick_e;

        if (ImGui::Button (ICON_FA_ROCKET " 快速选择"))
        {
            quick_s = date_se.GetSelectedDate ();
            quick_e = date_ee.GetSelectedDate ();
            ImGui::OpenPopup ("快速选择菜单");
        }

        if (ImGui::BeginPopup ("快速选择菜单"))
        {
            ImGui::AlignTextToFramePadding ();
            if (ImGui::CollapsingHeader ("当前日期", ImGuiTreeNodeFlags_DefaultOpen))
            {
                ImGui::BulletText ("开始时间：%s", quick_s.c_str ());
                ImGui::BulletText ("结束时间：%s", quick_e.c_str ());
            }
            if (ImGui::CollapsingHeader ("快速定位", ImGuiTreeNodeFlags_DefaultOpen))
            {
                if (ImGui::Button (ICON_FA_ARROW_LEFT " 上一年"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Year, AdjustType::Previous);
                }
                ImGui::SameLine ();
                if (ImGui::Button (ICON_FA_ARROW_DOWN " 本年度"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Year, AdjustType::Current);
                }
                ImGui::SameLine ();
                if (ImGui::Button (ICON_FA_ARROW_RIGHT " 下一年"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Year, AdjustType::Next);
                }

                if (ImGui::Button (ICON_FA_ARROW_LEFT " 上一季度"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Quarter, AdjustType::Previous);
                }
                ImGui::SameLine ();
                if (ImGui::Button (ICON_FA_ARROW_DOWN " 本季度"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Quarter, AdjustType::Current);
                }
                ImGui::SameLine ();
                if (ImGui::Button (ICON_FA_ARROW_RIGHT " 下一季度"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Quarter, AdjustType::Next);
                }

                if (ImGui::Button (ICON_FA_ARROW_LEFT " 上月"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Month, AdjustType::Previous);
                }
                ImGui::SameLine ();
                if (ImGui::Button (ICON_FA_ARROW_DOWN " 本月"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Month, AdjustType::Current);
                }
                ImGui::SameLine ();
                if (ImGui::Button (ICON_FA_ARROW_RIGHT " 下月"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Month, AdjustType::Next);
                }

                if (ImGui::Button (ICON_FA_ARROW_LEFT " 上一周"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Week, AdjustType::Previous);
                }
                ImGui::SameLine ();
                if (ImGui::Button (ICON_FA_ARROW_DOWN " 本周"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Week, AdjustType::Current);
                }
                ImGui::SameLine ();
                if (ImGui::Button (ICON_FA_ARROW_RIGHT " 下一周"))
                {
                    AdjustDateRange (quick_s, quick_e, Granularity::Week, AdjustType::Next);
                }
            }

            ImGui::SeparatorText ("注意");
            ImGui::TextUnformatted ("上... 是以开始日期为基准");
            ImGui::TextUnformatted ("本... 是以开始日期为基准");
            ImGui::TextUnformatted ("下... 是以结束日期为基准");

            if (ImGui::Button (ICON_FA_CHECK_SQUARE_O " 确定"))
            {
                date_s = quick_s;
                date_e = quick_e;
                date_se.SetData (Date::Parse (quick_s));
                date_ee.SetData (Date::Parse (quick_e));
                ImGui::CloseCurrentPopup ();
            }
            ImGui::EndPopup ();
        }
        ImGui::SameLine (0, ImGui::GetFontSize () * 2);
        bool const surveying = SurveyorRespondent::is_surveying ();
        ImGui::BeginDisabled (surveying);
        if (ImGui::Button ("发起一次调查"))
        {
            SurveyorRespondent::surveyor_range (date_s, date_e);
        }
        ImGui::EndDisabled ();
        if (surveying)
        {
            ImGui::SameLine ();
            ImGui::TextUnformatted ("正在等待调查结果...");
        }
        ImGui::TextUnformatted ("自定义总时间:");
        ImGui::SameLine ();
        ImGui::SetNextItemWidth (ImGui::GetFontSize () * 8);
        ImGui::InputDouble ("##CustomTotalTime", &d->custom_total, 0, 0, "%.2f");
        ImGui::SameLine ();
        if (ImGui::Button ("重置"))
            d->custom_total = 0;
        ImGui::SeparatorText ("调查结果");

        auto btnColr = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);

        for (auto &x: d->responds)
        {
            auto        &data  = x.second;
            double const total = d->custom_total == 0.0 ? data.hours : d->custom_total;

            ImGui::PushStyleColor(ImGuiCol_Header, btnColr);
            ImGui::PushStyleColor(ImGuiCol_HeaderHovered, btnColr);
            ImGui::PushStyleColor(ImGuiCol_HeaderActive, btnColr);

            const bool   open = ImGui::CollapsingHeader(x.first.c_str(), ImGuiTreeNodeFlags_DefaultOpen);

            ImGui::PopStyleColor(3);

            if (open)
            {
                ImGui::BulletText ("时间范围：%s 至 %s", data.date_start.c_str (), data.date_end.c_str ());
                ImGui::BulletText ("主机名：%s", data.hostname.c_str ());
                ImGui::BulletText ("用户名：%s", data.username.c_str ());
                if (data.hours == 0)
                {
                    ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (255, 255, 0, 255));
                    ImGui::BulletText ("无数据");
                    ImGui::PopStyleColor ();
                }
                else
                {
                    if (ImGui::BeginTable ("详细信息统计表", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit))
                    {
                        ImGui::TableSetupColumn ("标签", ImGuiTableColumnFlags_WidthStretch, 350);
                        ImGui::TableSetupColumn ("耗时");
                        ImGui::TableSetupColumn ("占比");
                        ImGui::TableHeadersRow ();

                        int    n           = 0;
                        double sum_of_time = 0.0;
                        for (auto const &x: data.tags)
                        {
                            ImGui::PushID (n++);
                            RenderTree (total, x);
                            ImGui::PopID ();
                            sum_of_time += x.total;  // 计算一级tag的时间和
                        }

                        if (sum_of_time < data.hours)
                        {
                            const auto diff = data.hours - sum_of_time;
                            // 这里有未分类的项
                            ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (255, 255, 0, 255));
                            ImGui::TableNextRow ();
                            ImGui::TableNextColumn ();
                            ImGui::BulletText ("无标签");
                            ImGui::TableNextColumn ();
                            ImGui::Text ("%7.2f小时（%.4f天）", diff, diff / 8.0);
                            ImGui::TableNextColumn ();
                            ImGui::Text ("%8.4f%%", diff / total * 100.0);
                            ImGui::PopStyleColor (1);
                        }

                        // 统计列
                        ImGui::TableNextRow ();
                        ImGui::TableNextColumn ();
                        ImGui::BulletText ("统计信息");
                        ImGui::TableNextColumn ();
                        ImGui::Text ("%7.2f小时（%.4f天）", data.hours, data.hours / 8.0);
                        ImGui::TableNextColumn ();
                        ImGui::Text ("%8.4f%%", data.hours / total * 100.0);

                        ImGui::EndTable ();
                    }
                }
            }
        }
    }
}

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE (RespondData::TagInfo, name, total, children)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE (RespondData, hostname, username, date_start, date_end, hours, tags)

static void ExecSurvey (const AppEvent::SurveyEvent &e)
{
    L_INFO("receive survey: %s ~ %s", e.date_start.c_str(), e.date_end.c_str());
    // 这里需要回复：主机名，用户名，统计数据
    // 统计数据包括：日期范围；标签、标签耗时；二级标签、耗时；
    RespondData respond {
            .hostname   = GetHostname (),
            .username   = GetUsername (),
            .date_start = e.date_start,
            .date_end   = e.date_end,
    };
    auto data     = App->Sql ()->GetStatisticsResult (e.date_start, e.date_end);
    respond.hours = data.total_time;

    for (auto const &x: data.tags)
    {
        RespondData::TagInfo info {
                .name  = x.tag_name,
                .total = x.tag_time,
        };
        for (auto const &y: x.sub_tags)
        {
            RespondData::TagInfo child {
                    .name  = y.tag_name,
                    .total = y.tag_time,
            };
            info.children.emplace_back (std::move (child));
        }
        respond.tags.emplace_back (std::move (info));
    }

    nlohmann::json obj = respond;
    auto content = obj.dump();
    L_DEBUG("respond content: %s", content.c_str());
    SurveyorRespondent::respondent_data (std::move(content));
}

SurveyorView::SurveyorView () : d {new Private}
{
    d->bus.listen<AppEvent::AppConfigEvent> ([this] (auto &) { d->LoadConfig (); });
    d->LoadConfig ();

    d->bus.listen<AppEvent::SurveyEvent> ([] (auto &survey) { ExecSurvey (survey); });
    d->bus.listen<AppEvent::RespondEvent> ([this] (auto &respond) {
        auto        j = nlohmann::json::parse (respond.content);
        RespondData data;
        j.get_to (data);
        d->StoreRespond (std::move (data));
    });
}

SurveyorView::~SurveyorView ()
{
    SurveyorRespondent::clean_up ();
    delete d;
}
